﻿using Sprout.Repository.Template.XParseEngine;
using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;

namespace Sprout.Repository
{
    public class StatementService
    {
        private static volatile StatementService _statementService;
        private static readonly object _locker = new object();


        /// <summary>
        /// 实例
        /// </summary>
        public static StatementService Instance
        {
            get
            {
                if (_statementService == null)
                {
                    lock (_locker)
                    {
                        if (_statementService == null)
                        {
                            _statementService = new StatementService();
                        }
                    }
                }
                return _statementService;
            }
        }

        /// <summary>
        /// 获取执行SQL
        /// </summary>
        /// <param name="statementId"></param>
        /// <param name="parameters"></param>
        /// <returns></returns>
        public string GetStatementSql(string statementId, object parameters)
        {
            if (string.IsNullOrEmpty(statementId))
            {
                return null;
            }
            var statement = DataSetting.Instance.GetTStatement(statementId);

            var parameterDic = GetParameterDic(parameters);

            string sql = GetStatementSql(statement, parameterDic);

            return sql;
        }
        /// <summary>
        /// 获取模板SQL
        /// </summary>
        /// <param name="statement"></param>
        /// <param name="parameterDic"></param>
        /// <returns></returns>
        public string GetStatementSql(TStatement statement, Dictionary<string, string> parameterDic)
        {
            string xml = statement.StatementXml;
            XmlDocument doc = new XmlDocument();
            doc.LoadXml(xml);
            StringBuilder sql = new StringBuilder();
            ParseDynamicTags(doc.DocumentElement, sql, parameterDic);

            foreach (var item in parameterDic)
            {
                sql = sql.Replace("$" + item.Key + "$", item.Value);
            }

            if (sql == null || string.IsNullOrEmpty(sql.ToString()))
            {
                throw new Exception("获取SQL失败");
            }

            return sql.ToString();
        }

        private void ParseDynamicTags(XmlNode node, StringBuilder sqlBuffer, Dictionary<string, string> parameterDic)
        {
            XmlNodeList childNodes = node.ChildNodes;

            foreach (XmlNode xmlNode in childNodes)
            {
                if (xmlNode.NodeType == XmlNodeType.CDATA || xmlNode.NodeType == XmlNodeType.Text)
                {
                    if ((xmlNode.NextSibling != null || xmlNode.PreviousSibling != null)
                        || (xmlNode.HasChildNodes == false && xmlNode.ParentNode.Name.ToLower() == "statement"))
                    {
                        string value = " " +
                              xmlNode.InnerText.Replace('\n', ' ').Replace('\r', ' ').Replace('\t', ' ').Trim();

                        sqlBuffer.Append(value);
                    }
                }
                else
                {
                    if (xmlNode.Name != "#comment")
                    {
                        string id = xmlNode.Name.ToLower();
                        var deSerializerFactory = new DeSerializerFactory();
                        IDeSerializer deSerializer = deSerializerFactory.GetDeSerializer(id);
                        if (deSerializer != null)
                        {
                            bool flag = deSerializer.Deserialize(xmlNode, sqlBuffer, parameterDic);
                            if (xmlNode.HasChildNodes && flag)
                            {
                                this.ParseDynamicTags(xmlNode, sqlBuffer, parameterDic);
                            }
                        }
                    }
                }
            }
        }

        /// <summary>
        /// 获取参数字典
        /// </summary>
        /// <param name="parameters">参数字典</param>
        /// <returns></returns>
        private Dictionary<string, string> GetParameterDic(object parameters)
        {
            var result = new Dictionary<string, string>();
            //如果传入的参数是数组对象，则后续不进行验证，用于批量新增、修改时使用
            if ((parameters is System.Collections.IEnumerable) && (parameters is string) == false)
            {
                return result;
            }
            if (parameters == null)
                return result;

            if (parameters is string)
                return result;

            if (parameters.GetType().IsValueType)
            {
                return result;
            }

            Type type = parameters.GetType();

            foreach (var property in type.GetProperties())
            {
                if (property.CanRead)
                {
                    var value = property.GetValue(parameters, null);
                    if (value == null)
                    {
                        result.Add(property.Name, null);
                    }
                    else if ((value is System.Collections.IEnumerable) && (value is string) == false)
                    {
                        //如果集合参数无值，将参数判断设为 null，以便进行 节点 是否为空的判断
                        bool haveData = false;
                        foreach (var item in value as System.Collections.IEnumerable)
                        {
                            haveData = true;
                            break;
                        }
                        if (haveData == false)
                        {
                            result.Add(property.Name, null);
                        }
                        else
                        {
                            result.Add(property.Name, "haveData");
                        }
                    }
                    else
                    {
                        if (value is Enum)
                        {
                            result.Add(property.Name, ((int)value).ToString());
                        }
                        else
                        {
                            result.Add(property.Name, value.ToString());
                        }
                    }
                }
            }

            return result;

        }
    }
}
